using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Xml.Serialization;

namespace ScottLane.VideoInbox
{
    /// <summary>
    /// A class that provides general video management activites such as downloading videos and
    /// organising shortcuts, thumbnails and the inbox.
    /// </summary>
    public class VideoManager
    {
        private ConfigurationSettings settings;

        /// <summary>
        /// Initializes a new instance of the VideoManager class.
        /// </summary>
        public VideoManager()
        {
            settings = ConfigurationSettings.Instance;
        }

        /// <summary>
        /// Checks each video source for new videos and playlists and creates shortcuts to those videos in the inbox.
        /// </summary>
        public void CheckForNewContent()
        {
            foreach (VideoSource source in settings.VideoSources)
            {
                DateTime lastChecked = DateTime.Now;
                List<Video> videos = source.GetNewVideos(source.LastChecked);

                foreach (Video video in videos)
                {
                    RemoveInvalidCharacters(video);

                    if (source.CreateInboxShortcut)
                    {
                        Shortcut inboxShortcut = new Shortcut(settings.InboxPath, video.Title, video.SourceLocationString);
                        video.Shortcuts.Add(inboxShortcut);
                    }

                    ProcessNewVideo(video);
                    OnVideoFound(new VideoEventArgs(video));
                }

                List<Playlist> playlists = source.GetNewPlaylists();

                foreach (Playlist playlist in playlists)
                {
                    ProcessNewPlaylist(playlist);
                    OnPlaylistFound(new PlaylistEventArgs(playlist));
                }

                source.LastChecked = lastChecked;
            }

            settings.Save();
        }

        /// <summary>
        /// Downloads the content referenced in the manifest.
        /// </summary>
        /// <param name="manifest">The manifest of videos to download.</param>
        public void DownloadContent(DownloadManifest manifest)
        {
            foreach (Download video in manifest.Downloads)
            {
                if (video.SourceExists && !video.DestinationExists) // todo - log error if source doesn't exist
                {
                    // change the download status video in the wvx file to 'downloading'
                    WvxFile wvx = WvxFile.Load(video.DestinationFileName);
                    wvx.Entries[0] = settings.DownloadingVideoFileName;
                    wvx.Save();

                    new WebClient().DownloadFile(video.SourceUrl, video.DestinationFileName);
                    wvx.Delete();

                    CreateShortcuts(video.Shortcuts);
                }
            }

            manifest.Delete();
        }

        /// <summary>
        /// Processes a new video, ready for use by Media Center.
        /// </summary>
        /// <param name="video">The video to process.</param>
        private void ProcessNewVideo(Video video)
        {
            if (video is DownloadableVideo)
            {
                DownloadableVideo download = (DownloadableVideo)video;

                if (!File.Exists(download.DownloadFileName))
                {
                    CreateDirectoryStructure(download);
                    CreateDownloadManifest(download);
                    DownloadThumbnail(download);
                    CreateWvxFile(download);
                }
            }

            CreateShortcuts(video.Shortcuts);
        }

        /// <summary>
        /// Creates the directory structure for a downloadable video.
        /// </summary>
        /// <param name="video">The video.</param>
        private void CreateDirectoryStructure(DownloadableVideo video)
        {
            FileInfo videoFile = new FileInfo(video.DownloadFileName);

            if (!videoFile.Directory.Exists)
            {
                videoFile.Directory.Create();
            }
        }

        /// <summary>
        /// Creates the download manifest for a downloadable video.
        /// </summary>
        /// <param name="video">The video.</param>
        private void CreateDownloadManifest(DownloadableVideo video)
        {
            DownloadManifest manifest = DownloadManifest.FromVideo(video);
            manifest.Create();
        }

        /// <summary>
        /// Downloads the video thumbnail.
        /// </summary>
        /// <param name="video">The video.</param>
        private void DownloadThumbnail(DownloadableVideo video)
        {
            if (!string.IsNullOrEmpty(video.ThumbnailUrl))
            {
                string extension = video.ThumbnailUrl.Substring(video.ThumbnailUrl.LastIndexOf("."));
                string thumbnailFileName = Path.ChangeExtension(video.DownloadFileName, extension);

                if (!File.Exists(thumbnailFileName))
                {
                    new WebClient().DownloadFile(video.ThumbnailUrl, thumbnailFileName);
                }
            }
        }

        /// <summary>
        /// Creates a wvx placeholder for a video in Media Center and returns the file name.
        /// </summary>
        /// <param name="video">The video.</param>
        private string CreateWvxFile(DownloadableVideo video)
        {
            WvxFile wvx = new WvxFile(video.DownloadFileName);
            wvx.Title = video.Title;
            wvx.Entries.Add(settings.NotDownloadedVideoFileName);
            wvx.Create();

            return wvx.FileName;
        }

        /// <summary>
        /// Creates the inbox shortcut and any additional shortcuts for a video.
        /// </summary>
        /// <param name="shortcuts">The shortcuts to create.</param>
        private void CreateShortcuts(List<Shortcut> shortcuts)
        {
            foreach (Shortcut shortcut in shortcuts)
            {
                if (shortcut.Exists)
                {
                    shortcut.Delete();
                }

                // if the target does not exist, it's assumed that it's not downloaded and a wvx file will be in its place.
                if (!File.Exists(shortcut.TargetPath))
                {
                    shortcut.TargetPath = new WvxFile(shortcut.TargetPath).FileName;
                }

                shortcut.Create();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="playlist">The playlist to process.</param>
        private void ProcessNewPlaylist(Playlist playlist)
        {

        }

        /// <summary>
        /// Removes any invalid characters from the paths in the specified video.
        /// </summary>
        /// <param name="video">The video to clean.</param>
        private void RemoveInvalidCharacters(Video video)
        {
            video.Title = CleanFileName(video.Title);

            if (video.SourceLocation.IsFile)
            {
                video.SourceLocationString = CleanPath(video.SourceLocationString);
            }

            foreach (Shortcut shortcut in video.Shortcuts)
            {
                shortcut.Directory = CleanPath(shortcut.Directory);
                shortcut.Name = CleanFileName(shortcut.Name);
                shortcut.TargetPath = CleanPath(shortcut.TargetPath);
            }

            if (video is DownloadableVideo)
            {
                DownloadableVideo download = (DownloadableVideo)video;
                download.DownloadFileName = CleanPath(download.DownloadFileName);
            }
        }

        /// <summary>
        /// Cleans a file name by removing any invalid characters.
        /// </summary>
        /// <param name="fileName">The file name to clean.</param>
        /// <returns>A clean file name.</returns>
        private string CleanFileName(string fileName)
        {
            char[] invalidChars = Path.GetInvalidFileNameChars();

            foreach (char invalidChar in invalidChars)
            {
                fileName = fileName.Replace(invalidChar.ToString(), string.Empty);
            }

            return fileName;
        }

        /// <summary>
        /// Cleans a path by removing any invalid characters.
        /// </summary>
        /// <param name="path">The path to clean.</param>
        /// <returns>A clean path.</returns>
        private string CleanPath(string path)
        {
            char[] invalidChars = Path.GetInvalidPathChars();

            foreach (char invalidChar in invalidChars)
            {
                path = path.Replace(invalidChar.ToString(), string.Empty);
            }

            return path;
        }

        //private void source_PlaylistFound(object sender, PlaylistEventArgs e)
        //{
        //    Playlist playlist = e.Playlist;

        //    // todo - create snapshot for playlist - use first if property not set, otherwise use property

        //    WvxFile playlistFile = new WvxFile(playlist.FileName);

        //    foreach (Video video in playlist.Videos)
        //    {
        //        string fileName = video.FileName;

        //        if (video is DownloadableVideo)
        //        {
        //            fileName = CreateWvxFile((DownloadableVideo)video);
        //        }

        //        playlistFile.Entries.Add(fileName); // todo - number the video titles in video shortcuts?
        //    }

        //    playlistFile.Title = playlist.Title;
        //    playlistFile.Create();
        //    CreateShortcut(settings.InboxPath, playlist.FileName, playlist.Title);
        //}

        #region Events

        /// <summary>
        /// The event that is raised when a new video is found.
        /// </summary>
        public event VideoEventHandler VideoFound;

        protected virtual void OnVideoFound(VideoEventArgs e)
        {
            if (VideoFound != null)
            {
                VideoFound(this, e);
            }
        }

        /// <summary>
        /// The event that is raised when a new video playlist is found.
        /// </summary>
        public event PlaylistEventHandler PlaylistFound;

        protected virtual void OnPlaylistFound(PlaylistEventArgs e)
        {
            if (PlaylistFound != null)
            {
                PlaylistFound(this, e);
            }
        }

        #endregion
    }
}
